package org.qengine.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.qengine.controller.UserExceptionController;
import org.qengine.dao.UserDao;
import org.qengine.entity.UserEntity;
import org.qengine.service.UserService;
import org.qengine.vo.LoginVo;
import org.qengine.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityNotFoundException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;

/**
 * 用户服务实现类，提供用户相关的业务逻辑操作。
 */
@Slf4j
@Service("UserService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {

    @Autowired
    private UserDao userDao;

    /**
     * 注册新用户，包括创建用户和保存到数据库。
     *
     * @param userEntity 用户实体信息
     */
    @Transactional
    public void registerUser(UserEntity userEntity) {
        addUser(userEntity);
    }

    /**
     * 生成一个随机盐值用于密码加密。
     *
     * @return Base64编码后的盐值字符串
     */
    private String generateSalt() {
        byte[] salt = new byte[16]; // 创建一个16字节长的数组用于存储盐值
        new SecureRandom().nextBytes(salt); // 使用SecureRandom类填充随机字节到salt数组
        return Base64.getEncoder().encodeToString(salt); // 将字节数组编码为Base64字符串并返回
    }

    /**
     * 使用提供的盐值对密码进行哈希处理。
     *
     * @param password 明文密码
     * @param salt     盐值
     * @return 哈希后的密码字符串
     */
    private String hashPasswordWithSalt(String password, String salt) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5"); // 获取MD5算法的消息摘要实例
            byte[] hash = md.digest((salt + password).getBytes()); // 将盐值与明文密码连接后计算其哈希值
            StringBuilder hexString = new StringBuilder(); // 创建StringBuilder对象用于构建十六进制字符串
            for (byte b : hash) { // 遍历每个字节
                String hex = Integer.toHexString(0xff & b); // 将每个字节转换为两位的十六进制字符串
                if (hex.length() == 1) hexString.append('0'); // 如果结果是单个字符，则补零
                hexString.append(hex); // 添加到最终的结果中
            }
            return hexString.toString(); // 返回完整的十六进制字符串表示的哈希值
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Error hashing password", e);
        }
    }

    /**
     * 校验用户登录凭据是否正确。
     *
     * @param loginVo 登录信息
     * @return 如果凭据正确则返回true，否则抛出异常
     */
    public boolean checkPassword(LoginVo loginVo) {
        // 参数校验
        if (loginVo == null || loginVo.getUserNickname() == null || loginVo.getUserPassword() == null) {
            throw new IllegalArgumentException("Username and password cannot be null");
        }

        // 查询用户信息
        UserEntity userEntity = this.lambdaQuery()
                .eq(UserEntity::getUserNickname, loginVo.getUserNickname())
                .one();

        // 检查用户是否存在以及是否有盐值
        if (userEntity == null || userEntity.getSalt() == null) {
            throw new IllegalArgumentException("Invalid credentials");
        }

        // 比较哈希后的输入密码与存储的密码
        String hashedInputPassword = hashPasswordWithSalt(loginVo.getUserPassword(), userEntity.getSalt());
        return hashedInputPassword.equals(userEntity.getUserPassword());
    }

    /**
     * 允许的角色列表（例如学生、老师、校长）。
     */
    private static final List<String> ALLOWED_ROLES = Arrays.asList("学生", "老师", "校长");

    /**
     * 添加新用户到系统中。
     *
     * @param userEntity 用户实体信息
     * @return 成功时返回true
     */
    @Transactional
    public boolean addUser(UserEntity userEntity) {
        validateUserEntity(userEntity); // 验证用户实体

        // 检查用户名是否已经存在
        if (this.existsByNickname(userEntity.getUserNickname())) {
            throw new UserExceptionController.DuplicateUserException("用户名已存在: " + userEntity.getUserNickname());
        }

        // 验证用户角色是否合法
        if (!ALLOWED_ROLES.contains(userEntity.getUserRole())) {
            throw new UserExceptionController.InvalidRoleException("无效的用户角色: " + userEntity.getUserRole());
        }

        // 校验非校长用户的班级信息
        if (!"校长".equals(userEntity.getUserRole()) &&
                (userEntity.getUserClass() == null || userEntity.getUserClass().trim().isEmpty())) {
            throw new IllegalArgumentException("对于非校长用户，班级信息不能为空");
        }

        // 生成并设置盐值和加密后的密码
        String salt = generateSalt();
        String hashedPassword = hashPasswordWithSalt(userEntity.getUserPassword(), salt);
        userEntity.setUserPassword(hashedPassword);
        userEntity.setSalt(salt);

        // 保存用户到数据库
        return this.save(userEntity);
    }

    /**
     * 验证用户实体的基本信息是否有效。
     *
     * @param userEntity 用户实体信息
     */
    private void validateUserEntity(UserEntity userEntity) {
        if (userEntity == null ||
                userEntity.getUserNickname() == null ||
                userEntity.getUserRealname() == null ||
                userEntity.getUserRole() == null) {
            throw new IllegalArgumentException("用户名、真实姓名和角色不能为空");
        }
    }

    /**
     * 检查指定昵称的用户是否存在。
     *
     * @param nickname 用户昵称
     * @return 如果存在返回true，否则返回false
     */
    private boolean existsByNickname(String nickname) {
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_nickname", nickname);
        return this.count(queryWrapper) > 0;
    }

    /**
     * 删除指定ID的用户。
     *
     * @param id 用户ID
     * @return 成功时返回true
     */
    @Transactional
    public boolean deleteUserById(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        UserEntity user = this.getById(id);
        if (user == null) {
            throw new EntityNotFoundException("未找到该用户: " + id);
        }
        return this.removeById(id);
    }

    /**
     * 根据用户名删除用户。
     *
     * @param userNickname 用户名
     * @return 成功时返回true
     */
    @Transactional
    public boolean deleteUserByUserNickname(String userNickname) {
        if (userNickname == null || userNickname.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_nickname", userNickname);
        UserEntity user = this.getOne(queryWrapper);

        if (user == null) {
            throw new EntityNotFoundException("未找到该用户: " + userNickname);
        }
        return this.remove(queryWrapper);
    }

    /**
     * 更新用户信息。
     *
     * @param vo 用户更新视图对象
     * @return 成功时返回true
     */
    @Transactional
    public boolean updateUser(UserVo vo) {
        if (vo == null || vo.getId() == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        UserEntity user = this.getById(vo.getId());
        if (user == null) {
            throw new EntityNotFoundException("未找到该用户: " + vo.getId());
        }
        updateFieldsFromVo(user, vo);
        return this.updateById(user);
    }

    /**
     * 将视图对象中的字段更新到用户实体中。
     *
     * @param user 用户实体
     * @param vo   视图对象
     */
    private void updateFieldsFromVo(UserEntity user, UserVo vo) {
        if (vo.getUserNickname() != null) user.setUserNickname(vo.getUserNickname());
        if (vo.getUserRealname() != null) user.setUserRealname(vo.getUserRealname());
        if (vo.getUserPassword() != null) {
            String salt = generateSalt();
            String hashedPassword = hashPasswordWithSalt(vo.getUserPassword(), salt);
            user.setUserPassword(hashedPassword);
            user.setSalt(salt);
        }
        if (vo.getUserClass() != null) user.setUserClass(vo.getUserClass());
        if (vo.getUserRole() != null) user.setUserRole(vo.getUserRole());
    }

    /**
     * 根据条件搜索用户，并分页显示结果。
     *
     * @param vo 搜索条件视图对象
     * @return 分页后的用户列表
     */
    @Override
    public IPage<UserEntity> search(UserVo vo) {
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();
        if (vo.getUserNickname() != null && !vo.getUserNickname().trim().isEmpty()) {
            queryWrapper.like("user_nickname", vo.getUserNickname());
        }

        int curPage = Optional.ofNullable(vo.getCurPage()).orElse(1);
        int limit = Optional.ofNullable(vo.getLimit()).orElse(10);
        IPage<UserEntity> page = new Page<>(curPage, limit);
        return userDao.selectPage(page, queryWrapper);
    }

    /**
     * 批量导入用户。
     *
     * @param users 用户实体列表
     */
    @Override
    @Transactional
    public void batchImportUsers(List<UserEntity> users) throws UserExceptionController.DuplicateUserException {
        if (users == null || users.isEmpty()) {
            throw new IllegalArgumentException("The list of users to import cannot be null or empty");
        }

        // 过滤掉无效用户，并准备数据
        List<UserEntity> validUsers = new ArrayList<>();
        List<String> failedUserNicknames = new ArrayList<>();

        for (UserEntity user : users) {
            if (!validateAndPrepareUser(user)) {
                failedUserNicknames.add(user.getUserNickname());
            } else {
                validUsers.add(user);
            }
        }

        if (!failedUserNicknames.isEmpty()) {
            String errorMessage = "以下用户的昵称已存在: " + String.join(", ", failedUserNicknames);
            log.warn(errorMessage);
            throw new UserExceptionController.DuplicateUserException(errorMessage);
        }

        // 批量保存用户
        if (!validUsers.isEmpty()) {
            this.saveBatch(validUsers);
        }
    }

    /**
     * 验证用户数据并准备保存。
     *
     * @param userEntity 用户实体信息
     * @return 如果验证通过则返回true，否则返回false
     */
    private boolean validateAndPrepareUser(UserEntity userEntity) {
        try {
            validateUserEntity(userEntity);
            if (this.existsByNickname(userEntity.getUserNickname())) {
                return false;
            }
            String salt = generateSalt();
            String hashedPassword = hashPasswordWithSalt(userEntity.getUserPassword(), salt);
            userEntity.setUserPassword(hashedPassword);
            userEntity.setSalt(salt);
            return true;
        } catch (IllegalArgumentException e) {
            log.warn("Invalid user data: {}. Reason: {}", userEntity.getUserNickname(), e.getMessage());
            return false;
        }
    }

    /**
     * 获取指定用户的用户角色。
     *
     * @param userId 用户ID
     * @return 用户角色名称
     */
    public String getUserRoleById(Long userId) {
        UserEntity user = this.getById(userId);
        if (user == null) {
            throw new EntityNotFoundException("User not found with ID: " + userId);
        }
        return user.getUserRole();
    }
}